﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media.Animation;
using System.Windows.Media.Media3D;
using System.Xml;

using KELT.ErrorControl;
using System.Threading;
using KELT.KELTObjects;

namespace KELT
{
    public class CameraManager
    {
        public CameraManager()
        {
            m_pageList = new PageCollection();
        }

        public TOOL mainContext;

        private PerspectiveCamera pCamera;
        private Point3D position3D = new Point3D(0, 0, 50);
        private TranslateTransform3D myTranslateTransform3D;

        #region Page
        //public ArrayList PageList = new ArrayList();
        private readonly PageCollection m_pageList;
        public PageCollection pageList
        {
            get
            {
                return m_pageList;
            }
        }

        public void AddPage(Page page)
        {
            m_pageList.Add(page);
        }
        #endregion

        public Page selectObject;

        public Storyboard cameraStoryboard;

        //타겟 고유번호
        public int INDEXNUMBER = 0;

        //카메라 이동, 선택시 사용
        private Point mousePreXY = new Point(0, 0);
        private bool isWheelPressed = false;
        private bool isSelect = false;
        
        public void init(TOOL mainContext)
        {
            if(this.mainContext == null)
            {
                this.mainContext = mainContext;
                this.pCamera = mainContext.viewportCam;
                this.pCamera.Position = position3D;

                Transform3DGroup cameraTransform3DGroup = MakeCameraTransform3DGroup(new Point3D(0, 0, 1));
                this.pCamera.Transform = cameraTransform3DGroup;
            }

            this.cameraStoryboard = new Storyboard();
            cameraStoryboard.Completed += cameraStorybard_Completed;
            cameraStoryboard.CurrentTimeInvalidated += myStoryboard_Changed;
        }

        void myStoryboard_Changed(object sender, EventArgs e)
        {
            Clock storyboardClock = (Clock)sender;

            if (storyboardClock.CurrentTime != null)
            {
                foreach (Page page in pageList)
                {
                    
                    page.ChkObjet(mainContext, storyboardClock.CurrentTime.Value);
                }
            }


        }

        void cameraStorybard_Completed(object sender, EventArgs e)
        {
            cameraStoryboard.Stop(mainContext);

            foreach (Page page in pageList)
            {

                page.isSet = false;
            }

        }

        public void makePage()
        {
            double x = mainContext.mainViewport.RenderSize.Width;
            double y = mainContext.mainViewport.RenderSize.Height;

            CreatePage(x, y, new Vector3D(0, 0, 0), cameraIndex);

            //RegistCameraAnimation();
            //moveCameraAnimation();
            cameraIndex++;
        }

        private Transform3DGroup MakeCameraTransform3DGroup(Point3D point3D)
        {
            Transform3DGroup cameraTransform3DGroup = new Transform3DGroup();
            myTranslateTransform3D = new TranslateTransform3D(point3D.X, point3D.Y, point3D.Z);
            
            ScaleTransform3D myScaleTransform3D = new ScaleTransform3D(1, 1, 1);
            myScaleTransform3D.CenterX = point3D.X;
            myScaleTransform3D.CenterY = point3D.Y;
            myScaleTransform3D.CenterZ = point3D.Z;


            RotateTransform3D myRotateTransform = new RotateTransform3D();
            myRotateTransform.CenterX = point3D.X;
            myRotateTransform.CenterY = point3D.Y;
            myRotateTransform.CenterZ = point3D.Z;

            myRotateTransform.Rotation = new AxisAngleRotation3D { Angle = 0, Axis = new Vector3D(0, 0, 0) };
            mainContext.RegisterName("CameraTranslate", myTranslateTransform3D);
            mainContext.RegisterName("CameraRotate", myRotateTransform);

            cameraTransform3DGroup.Children.Add(myTranslateTransform3D);
            cameraTransform3DGroup.Children.Add(myScaleTransform3D);
            cameraTransform3DGroup.Children.Add(myRotateTransform);

            return cameraTransform3DGroup;
        }

        public void GetCameraXML(XmlWriter writer)
        {
            try
            {
                writer.WriteStartElement("CameraManager");
                {

                }
                writer.WriteEndElement();
            }
            catch (Exception e)
            {
                ErrorManager.Instance.RecordError(e);
            }

        }

        #region Mouse

        public void MouseDownObject(object sender, MouseButtonEventArgs e)
        {
            foreach (Page target in pageList)
            {
                String name = (target.getTargetNmae());
                if (name.Equals((sender as Viewbox).Name))
                {
                    selectObject = target;
                    isSelect = true;
                    break;
                }
            }

            if (isSelect == false)
            {
                Mouse.Capture((sender as Viewbox));
            }
        }

        public void MouseMoveObject()
        {
            if (selectObject != null && isSelect)
                selectObject.setXYZ(mainContext, mainContext.mainViewport.RenderSize, Mouse.GetPosition(mainContext.mainViewport).X, Mouse.GetPosition(mainContext.mainViewport).Y, 0);

        }


        public void MouseUpObject()
        {
            if (isSelect)
            {
                selectObject = null;
                isSelect = false;
                Mouse.Capture(null);
                return;
            }
        }

        public void MouseWheelDown(Point point)
        {
            mousePreXY.X = point.X;
            mousePreXY.Y = point.Y;
            isWheelPressed = true;
        }

        public void MouseWheelMove(Point point)
        {
            if (isWheelPressed)
            {
                double xDelta = mousePreXY.X - point.X;
                double yDelta = mousePreXY.Y - point.Y;

                CameraMove(xDelta, yDelta);
                mousePreXY = point;
            }
        }

        public void MouseWheelUp(Point point)
        {
            if (isWheelPressed)
            {
                isWheelPressed = false;
                mousePreXY = point;
            }
        }
        #endregion

        public void CameraZoomIn()
        {
            position3D.Z += 5;
            pCamera.Position = position3D;
        }

        public void CameraZoomOut()
        {
            position3D.Z -= 5;
            pCamera.Position = position3D;
        }

        public void CameraZoom(double zoom)
        {
            if (zoom > 0)
            {
                position3D.Z--;
            }

            if (zoom < 0)
            {
                position3D.Z++;
            }
            pCamera.Position = position3D;
        }

        public void CameraMove(double xDeltha, double yDelta)
        {
            if (xDeltha < 0)
            {
                position3D.X += (xDeltha / 10);
            }
            else
            {
                position3D.X += (xDeltha / 10);
            }
            if (yDelta < 0)
            {
                position3D.Y += (yDelta / 10);
            }
            else
            {
                position3D.Y += (yDelta / 10);
            }
            pCamera.Position = position3D;

        }


        public void RegistCameraAnimation()
        {


            cameraStoryboard.Children.Clear();
            
            Point3D prevPoint ;
            if (pageList.Count > 0)
                prevPoint = (pageList[0] as Page).getPosition();
            else
                prevPoint = new Point3D();

            foreach (Page target in pageList)
            {
                //if (target == TargetList[0]) continue;

                DoubleAnimation doubleXAnimaation = new DoubleAnimation();
                doubleXAnimaation.Duration = new Duration(TimeSpan.FromSeconds(target.endTime - target.startTime));
                doubleXAnimaation.From = prevPoint.X - position3D.X;
                doubleXAnimaation.To = target.getPosition().X - position3D.X;
                doubleXAnimaation.BeginTime = TimeSpan.FromSeconds((target.startTime));
                doubleXAnimaation.AccelerationRatio = 1;

                Storyboard.SetTargetProperty(doubleXAnimaation, new PropertyPath(TranslateTransform3D.OffsetXProperty));
                Storyboard.SetTargetName(doubleXAnimaation, "CameraTranslate");
                cameraStoryboard.Children.Add(doubleXAnimaation);

                DoubleAnimation doubleYAnimaation = new DoubleAnimation();
                doubleYAnimaation.Duration = new Duration(TimeSpan.FromSeconds(target.endTime - target.startTime));
                doubleYAnimaation.From = prevPoint.Y - position3D.Y;
                doubleYAnimaation.To = target.getPosition().Y - position3D.Y;
                doubleYAnimaation.BeginTime = TimeSpan.FromSeconds(target.startTime);
                doubleXAnimaation.AccelerationRatio = 1;

                Storyboard.SetTargetProperty(doubleYAnimaation, new PropertyPath(TranslateTransform3D.OffsetYProperty));
                Storyboard.SetTargetName(doubleYAnimaation, "CameraTranslate");

                cameraStoryboard.Children.Add(doubleYAnimaation);

                //CameraCenter도 이동
                DoubleAnimation doubleCenterXAnimaation = new DoubleAnimation();
                doubleCenterXAnimaation.Duration = new Duration(TimeSpan.FromSeconds(0.1));
                doubleCenterXAnimaation.From = prevPoint.X;
                doubleCenterXAnimaation.To = target.getPosition().X;
                doubleCenterXAnimaation.BeginTime = TimeSpan.FromSeconds(target.endTime);
                doubleCenterXAnimaation.AccelerationRatio = 1;

                Storyboard.SetTargetProperty(doubleCenterXAnimaation, new PropertyPath(RotateTransform3D.CenterXProperty));
                Storyboard.SetTargetName(doubleCenterXAnimaation, "CameraRotate");

                cameraStoryboard.Children.Add(doubleCenterXAnimaation);


                DoubleAnimation doubleCenterYAnimaation = new DoubleAnimation();
                doubleCenterYAnimaation.Duration = new Duration(TimeSpan.FromSeconds(0.1));
                doubleCenterYAnimaation.From = prevPoint.Y;
                doubleCenterYAnimaation.To = target.getPosition().Y;
                doubleCenterYAnimaation.BeginTime = TimeSpan.FromSeconds(target.endTime);
                doubleCenterYAnimaation.AccelerationRatio = 1;

                Storyboard.SetTargetProperty(doubleCenterYAnimaation, new PropertyPath(RotateTransform3D.CenterYProperty));
                Storyboard.SetTargetName(doubleCenterYAnimaation, "CameraRotate");

                cameraStoryboard.Children.Add(doubleCenterYAnimaation);


                Vector3D vector3D = target.getCameraVector();
                Rotation3DAnimationUsingKeyFrames doubleRAnimaation = new Rotation3DAnimationUsingKeyFrames();
                doubleRAnimaation.Duration = new Duration(TimeSpan.FromSeconds(target.rotateEndTime - target.rotateStartTime));
                doubleRAnimaation.BeginTime = TimeSpan.FromSeconds(target.rotateStartTime);
                doubleRAnimaation.AccelerationRatio = 1;
                doubleRAnimaation.KeyFrames.Add(
                   new LinearRotation3DKeyFrame(
                       new AxisAngleRotation3D(vector3D, 0),
                       KeyTime.FromTimeSpan(TimeSpan.FromSeconds(0))) // KeyTime
                   );

                doubleRAnimaation.KeyFrames.Add(
                 new LinearRotation3DKeyFrame(
                     new AxisAngleRotation3D(vector3D, 120),
                    KeyTime.FromTimeSpan(TimeSpan.FromSeconds(0.2))) // KeyTime
                );
                doubleRAnimaation.KeyFrames.Add(
                 new LinearRotation3DKeyFrame(
                     new AxisAngleRotation3D(vector3D, 240),
                    KeyTime.FromTimeSpan(TimeSpan.FromSeconds(0.3))) // KeyTime
                );
                doubleRAnimaation.KeyFrames.Add(
                 new LinearRotation3DKeyFrame(
                     new AxisAngleRotation3D(vector3D, 360),
                    KeyTime.FromTimeSpan(TimeSpan.FromSeconds(0.4))) // KeyTime
                );



                Storyboard.SetTargetProperty(doubleRAnimaation, new PropertyPath(RotateTransform3D.RotationProperty));
                Storyboard.SetTargetName(doubleRAnimaation, "CameraRotate");

                cameraStoryboard.Children.Add(doubleRAnimaation);


                prevPoint = target.getPosition();
            }

        }

        public void moveCameraAnimation()
        {
            cameraStoryboard.Children.Clear();

            Point from = new Point(), to = new Point();
            if(pageList.Count==1)
            {
                from.X = position3D.X;
                from.Y = position3D.Y;

                to.X = (pageList[pageList.Count - 1] as Page).getPosition().X - position3D.X;
                to.Y = (pageList[pageList.Count - 1] as Page).getPosition().Y - position3D.Y;
            }
            else
            {
                from.X = (pageList[pageList.Count - 2] as Page).getPosition().X;
                from.Y = (pageList[pageList.Count - 2] as Page).getPosition().Y;

                to.X = (pageList[pageList.Count - 1] as Page).getPosition().X - position3D.X;
                to.Y = (pageList[pageList.Count - 1] as Page).getPosition().Y - position3D.Y;

            }
                //if (target == TargetList[0]) continue;
            double durationTime = 0.5;
                DoubleAnimation doubleXAnimaation = new DoubleAnimation();
                doubleXAnimaation.Duration = new Duration(TimeSpan.FromSeconds(durationTime));
                doubleXAnimaation.From = from.X;
                doubleXAnimaation.To = to.X;
                doubleXAnimaation.BeginTime = TimeSpan.FromSeconds((0));
                doubleXAnimaation.AccelerationRatio = 1;

                Storyboard.SetTargetProperty(doubleXAnimaation, new PropertyPath(TranslateTransform3D.OffsetXProperty));
                Storyboard.SetTargetName(doubleXAnimaation, "CameraTranslate");
                cameraStoryboard.Children.Add(doubleXAnimaation);

                DoubleAnimation doubleYAnimaation = new DoubleAnimation();
                doubleYAnimaation.Duration = new Duration(TimeSpan.FromSeconds(durationTime));
                doubleYAnimaation.From = from.Y;
                doubleYAnimaation.To = to.Y;
                doubleYAnimaation.BeginTime = TimeSpan.FromSeconds(0);
                doubleXAnimaation.AccelerationRatio = 1;

                Storyboard.SetTargetProperty(doubleYAnimaation, new PropertyPath(TranslateTransform3D.OffsetYProperty));
                Storyboard.SetTargetName(doubleYAnimaation, "CameraTranslate");

                cameraStoryboard.Children.Add(doubleYAnimaation);


                //CameraCenter도 이동
                DoubleAnimation doubleCenterXAnimaation = new DoubleAnimation();
                doubleCenterXAnimaation.Duration = new Duration(TimeSpan.FromSeconds(0.1));
                doubleCenterXAnimaation.From = position3D.X;
                doubleCenterXAnimaation.To = (pageList[pageList.Count - 1] as Page).getPosition().X;
                doubleCenterXAnimaation.BeginTime = TimeSpan.FromSeconds(durationTime);
                doubleCenterXAnimaation.AccelerationRatio = 1;

                Storyboard.SetTargetProperty(doubleCenterXAnimaation, new PropertyPath(RotateTransform3D.CenterXProperty));
                Storyboard.SetTargetName(doubleCenterXAnimaation, "CameraRotate");

                cameraStoryboard.Children.Add(doubleCenterXAnimaation);


                DoubleAnimation doubleCenterYAnimaation = new DoubleAnimation();
                doubleCenterYAnimaation.Duration = new Duration(TimeSpan.FromSeconds(0.1));
                doubleCenterYAnimaation.From = position3D.Y;
                doubleCenterYAnimaation.To = (pageList[pageList.Count - 1] as Page).getPosition().Y;
                doubleCenterYAnimaation.BeginTime = TimeSpan.FromSeconds(durationTime);
                doubleCenterYAnimaation.AccelerationRatio = 1;

                Storyboard.SetTargetProperty(doubleCenterYAnimaation, new PropertyPath(RotateTransform3D.CenterYProperty));
                Storyboard.SetTargetName(doubleCenterYAnimaation, "CameraRotate");

                cameraStoryboard.Children.Add(doubleCenterYAnimaation);


            cameraStoryboard.Begin(mainContext, true);

        }
        public void moveCameraAnimation(int index)
        {
            cameraStoryboard.Children.Clear();

            Point from = new Point(), to = new Point();


            to.X = (pageList[index] as Page).getPosition().X - position3D.X;
            to.Y = (pageList[index] as Page).getPosition().Y - position3D.Y;


            //if (target == TargetList[0]) continue;
            double durationTime = 0.5;
            DoubleAnimation doubleXAnimaation = new DoubleAnimation();
            doubleXAnimaation.Duration = new Duration(TimeSpan.FromSeconds(durationTime));
            doubleXAnimaation.To = to.X;
            doubleXAnimaation.BeginTime = TimeSpan.FromSeconds((0));
            doubleXAnimaation.AccelerationRatio = 1;

            Storyboard.SetTargetProperty(doubleXAnimaation, new PropertyPath(TranslateTransform3D.OffsetXProperty));
            Storyboard.SetTargetName(doubleXAnimaation, "CameraTranslate");
            cameraStoryboard.Children.Add(doubleXAnimaation);

            DoubleAnimation doubleYAnimaation = new DoubleAnimation();
            doubleYAnimaation.Duration = new Duration(TimeSpan.FromSeconds(durationTime));
            doubleYAnimaation.To = to.Y;
            doubleYAnimaation.BeginTime = TimeSpan.FromSeconds(0);
            doubleXAnimaation.AccelerationRatio = 1;

            Storyboard.SetTargetProperty(doubleYAnimaation, new PropertyPath(TranslateTransform3D.OffsetYProperty));
            Storyboard.SetTargetName(doubleYAnimaation, "CameraTranslate");

            cameraStoryboard.Children.Add(doubleYAnimaation);


            //CameraCenter도 이동
            DoubleAnimation doubleCenterXAnimaation = new DoubleAnimation();
            doubleCenterXAnimaation.Duration = new Duration(TimeSpan.FromSeconds(0.1));
            doubleCenterXAnimaation.To = (pageList[pageList.Count - 1] as Page).getPosition().X;
            doubleCenterXAnimaation.BeginTime = TimeSpan.FromSeconds(durationTime);
            doubleCenterXAnimaation.AccelerationRatio = 1;

            Storyboard.SetTargetProperty(doubleCenterXAnimaation, new PropertyPath(RotateTransform3D.CenterXProperty));
            Storyboard.SetTargetName(doubleCenterXAnimaation, "CameraRotate");

            cameraStoryboard.Children.Add(doubleCenterXAnimaation);


            DoubleAnimation doubleCenterYAnimaation = new DoubleAnimation();
            doubleCenterYAnimaation.Duration = new Duration(TimeSpan.FromSeconds(0.1));
            doubleCenterYAnimaation.To = (pageList[pageList.Count - 1] as Page).getPosition().Y;
            doubleCenterYAnimaation.BeginTime = TimeSpan.FromSeconds(durationTime);
            doubleCenterYAnimaation.AccelerationRatio = 1;

            Storyboard.SetTargetProperty(doubleCenterYAnimaation, new PropertyPath(RotateTransform3D.CenterYProperty));
            Storyboard.SetTargetName(doubleCenterYAnimaation, "CameraRotate");

            cameraStoryboard.Children.Add(doubleCenterYAnimaation);


            cameraStoryboard.Begin(mainContext, true);

        }


        int cameraIndex = 0;


        public Page CreatePage()
        {
            Random rand = new Random();
            double x = 0;// rand.Next(0, 800);
            double y = 0;// rand.Next(-0, 800);
            Thread.Sleep(100);
            GridLength width = mainContext.EncoderWindow.ColumnDefinitions.ElementAt(1).Width;
            x = mainContext.mainViewport.RenderSize.Width/2;
            y = mainContext.mainViewport.RenderSize.Height/2;
            
            Page page;

            if(cameraIndex == 0)
            {
                page = CreatePage(x, y, new Vector3D(0, 0, 0), cameraIndex);
            }
            else
            {
                Page t = pageList[pageList.Count - 1] as Page;
                page = CreatePage(t.getPosition().X + x, t.getPosition().Y + y, new Vector3D(0, 0, 0), cameraIndex);
            }

            //RegistCameraAnimation();
            //moveCameraAnimation();
            cameraIndex++;

            return page;
        }

        public Page CreatePage(double x, double y, Vector3D vector, double time)
        {
            double endTime = 0;

            for (int i = 0; i < mainContext.viewPortMgr.ObjectList.Count; i++)
            {
                if (endTime < mainContext.viewPortMgr.ObjectList.ElementAt(i).endTime)
                {
                    endTime = mainContext.viewPortMgr.ObjectList.ElementAt(i).endTime;
                }
            }

            Page testTarget = new Page(mainContext, INDEXNUMBER, x, y);

            testTarget.setTime(endTime, endTime + 0.5);
            testTarget.setRotateTime(endTime + 0.5, endTime + 1);

            testTarget.setCameraVector(vector);
            setCamera(testTarget);

            INDEXNUMBER++;

            return testTarget;
        }

        public void DeleteTarget(Page target)
        {
            if (pageList.Count > 0)
            {
                int index = pageList.IndexOf(target);
                pageList[index] = null;
                pageList.Remove(target);

                for (int i = 0; i < pageList.Count; i++)
                {
                    (pageList[i] as Page).indexNumber = i;
                }
            }
        }

        public void setZoomMag(int mag = 50)
        {
            Point3DAnimation camPosAnim = new Point3DAnimation(new Point3D(position3D.X, position3D.Y, mag), TimeSpan.FromSeconds(0.5));
            pCamera.BeginAnimation(PerspectiveCamera.PositionProperty, camPosAnim);
        }

        public void setCamera(Page target) 
        {
            AddPage(target);
            //pageList.Add(target); 
        }

        public TranslateTransform3D getTranslateTransform() 
        { 
            return myTranslateTransform3D; 
        }

        public Point3D getPoint3D() 
        { 
            return position3D; 
        }



        public void getStoryboard(TimeSpan seekTime)
        {
            cameraStoryboard.Begin(mainContext, true); // if the second parameter is missing or false, it doesn't work
            cameraStoryboard.Seek(mainContext, seekTime, TimeSeekOrigin.BeginTime);
            cameraStoryboard.Pause(mainContext); // stop doesn't work

        }

        public void Pause()
        {
            cameraStoryboard.Pause(mainContext);
        }

        public void Start()
        {
            if (pageList.Count > 0)
            {
                position3D = (pageList[pageList.Count - 1] as Page).getPosition();
                position3D.Z = 50;
                pCamera.Position = position3D;
                RegistCameraAnimation();
            }

                cameraStoryboard.Stop(mainContext);
            cameraStoryboard.Begin(mainContext);
           
        }

        public void Skip()
        {
            cameraStoryboard.SkipToFill(mainContext);
            cameraStoryboard.Stop(mainContext);
        }

        public void Stop()
        {
            cameraStoryboard.Stop(mainContext);
        }

    }
}
